package com.livgo.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.livgo.dao.dto.UploadFileDto;
import com.livgo.dao.po.*;
import com.livgo.dao.vo.FileInfoVo;
import com.livgo.mapper.FileMapper;
import com.livgo.service.DownloadInfoService;
import com.livgo.service.FileService;
import com.livgo.utils.IpUtils;

import jakarta.servlet.http.HttpServletRequest;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;

@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, FileInfo> implements FileService {

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private DownloadInfoService downloadInfoService;

    @Autowired
    private SecretKeyServiceImpl secretKeyService;

    @Autowired
    private FileStorageService fileStorageService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String upload(UploadFileDto uploadFileDto) {

        Snowflake snowflake = new Snowflake();

        if (uploadFileDto.getType() == null) {
            return "请选择上传类型！";
        }

        if (uploadFileDto.getType() == 0 && uploadFileDto.getMaxCount() == null) {
            return "请选择最大下载次数！";
        }

        if (uploadFileDto.getType() == 1 && uploadFileDto.getExpireTime() == null) {
            return "请选择文件有效期！";
        }

        if (uploadFileDto.getType() == 2 && uploadFileDto.getSecretKey() == null) {
            return "请输入密钥！";
        }

        if (uploadFileDto.getSecretKey() != null &&
                secretKeyService.getById(uploadFileDto.getSecretKey()) == null) {
            return "密钥不存在！";
        }

        String path = new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/";

        String code = RandomUtil.randomString(6);

        org.dromara.x.file.storage.core.FileInfo upload = fileStorageService.of(uploadFileDto.getFile())
                .setPath(path)
                .upload();

        FileInfo info = new FileInfo();

        info.setOriName(upload.getOriginalFilename());

        info.setFileName(upload.getFilename());

        info.setUri(path);

        info.setFileSize(upload.getSize().toString());

        info.setCode(code);

        info.setType(uploadFileDto.getType());

        info.setUploadTime(LocalDateTime.now());

        info.setExtension(upload.getExt());

        if (uploadFileDto.getType() == 0) {
            info.setMaxCount(uploadFileDto.getMaxCount());
            info.setCurrentCount(0);
        }

        if (uploadFileDto.getType() == 1) {
            info.setExpireTime(uploadFileDto.getExpireTime());
        }
        info.setStatus(0);
        fileMapper.insert(info);
        return code;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseEntity<byte[]> download(Integer id, HttpServletRequest request) {

        String ip = IpUtils.getClientIp(request);

        FileInfo info = fileMapper.selectById(id);

        LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);

        LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);

        if (info == null) {
            return ResponseEntity
                    .notFound()
                    .build();
        }

        LambdaQueryWrapper<DownloadInfo> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(DownloadInfo::getIpAddress, ip)
                .between(DownloadInfo::getDownloadTime, start, end);

        if (downloadInfoService.list( wrapper).size() >= 3) {
            return ResponseEntity
                    .status(429)
                    .build();
        }

        DownloadInfo downloadInfo = new DownloadInfo();

        downloadInfo.setFileId(info.getId());

        downloadInfo.setDownloadTime(LocalDateTime.now());

        downloadInfo.setIpAddress(ip);

        downloadInfoService.save(downloadInfo);

        org.dromara.x.file.storage.core.FileInfo fileInfo = new org.dromara.x.file.storage.core.FileInfo();

        fileInfo.setPlatform("webdav-1")
                .setBasePath("webdav/")
                .setPath(info.getUri())
                .setFilename(info.getFileName());

        if (info.getType() == 1 && info.getExpireTime().isBefore(LocalDateTime.now())) {
            fileStorageService.delete(fileInfo);
            fileMapper.deleteById(info.getId());
            return ResponseEntity.notFound()
                    .build();
        }

        byte[] bytes = fileStorageService.download(fileInfo).bytes();

        if (info.getType() == 2) {
            info.setCurrentCount(info.getCurrentCount() + 1);
            fileMapper.updateById(info);
        }

        if (info.getType() == 0) {
            int currentCount = info.getCurrentCount();
            if (currentCount +1 ==  info.getMaxCount()) {
                fileStorageService.delete(fileInfo);
                fileMapper.deleteById(info.getId());
            } else {
                info.setCurrentCount(currentCount + 1);
                fileMapper.updateById(info);
            }
        }

        HttpHeaders headers = new HttpHeaders();

        headers.add("Content-Disposition",
                "attachment;filename=" +
                        URLEncoder.encode(info.getOriName(),
                                StandardCharsets.UTF_8)
                                .replace("+", "%20"));
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentLength(bytes.length);

        return ResponseEntity.ok()
                .headers(headers)
                .body(bytes);
    }

    @Override
    public PageBean<FileInfoVo> listPage(Integer pageNum, Integer pageSize) {

        PageBean<FileInfoVo> pb = new PageBean<>();

        PageHelper.startPage(pageNum, pageSize);

        List<FileInfoVo> list = fileMapper.listPage();

        Page<FileInfoVo> p = (Page<FileInfoVo>) list;

        pb.setTotal((int) p.getTotal());

        pb.setData(p.getResult());

        return pb;
    }

    @Override
    public FileInfoVo info(String code) {
        return fileMapper.info(code);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result delete(Integer id, String secretKey) {
        SecretKey sk = secretKeyService.getById(secretKey);
        if (sk == null) {
            return Result.error("密钥不存在！");
        }
        FileInfo info = fileMapper.selectById(id);

        if (info == null) {
            return Result.error("文件不存在！");
        }

        fileMapper.deleteById(info);

        org.dromara.x.file.storage.core.FileInfo fileInfo = new org.dromara.x.file.storage.core.FileInfo();

        fileInfo.setPlatform("webdav-1")
                .setBasePath("webdav/")
                .setPath(info.getUri())
                .setFilename(info.getFileName());

        fileStorageService.delete(fileInfo);

        return Result.success();
    }
}
